/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package com.thesis.algorithm;

import java.util.*;


/**
 *
 * @author Administrator
 */
public class HMM {
    // data from many files

    LinkedList<Sequence> listOfSequences = new LinkedList<Sequence>();
    HMMMatrices hmmParameters = new HMMMatrices();

    
    public void emitAll ()
    {
        hmmParameters.emitAll();
    }
    
    public String toString ()
    {
        return hmmParameters.toString();
    }
    // 1, calculate A,B using counting;
    // 2, calculate A,B using Baum-Welch reestimation: decided not to do
    // File format: State Observation \n State Observation
//    public void train(String[] filePath) {
//        //change in hmmParameters
//        // read observation - state
//        for (int i = 0; i < filePath.length; i++) {
//            Sequence temp = new Sequence();
//            temp.readFromFile(filePath[i]);
//            if (temp.size() != 0) {
//                listOfSequences.add(temp);
//            }
//        }
//
//        for (Sequence seq : listOfSequences) {
//            hmmParameters.addSequence(seq);
//        }
//
//
//    }
    
    public void train(LinkedList<Sequence> seqs)
    {
    	for (Sequence seq : seqs) {
            hmmParameters.addSequence(seq);
        }
    }

    public Sequence predict(Sequence input) // input is a sequence with empty seqmentid
    {
        return Viterbi(input);
    }


    private Sequence Viterbi(Sequence inp) {
    	Sequence input = new Sequence();
    	for (SequenceElement seqel : inp.sequenceinFile)
    	{
    		input.add(new SequenceElement(seqel.Observation, seqel.State));
    		
    	}
        if (input.size() < 2) {
            System.err.println("input sequence less than 2");
            return null;
        }
        //============================ Initialization
        LinkedList<Hashtable<Character, Double>> delta = new LinkedList<Hashtable<Character, Double>>();
        LinkedList<Hashtable<Character, Character>> psi = new LinkedList<Hashtable<Character, Character>>();


        char currState;
        double tmpDelta;

        Hashtable<Character, Double> deltaElement = new Hashtable<Character, Double>();
        Hashtable<Character, Character> psiElement = new Hashtable<Character, Character>();

        // traverse firststatecount
        //TODO: change traverse system from PI to B - done
        
        
        if (hmmParameters.observationSTStateCount.get(input.sequenceinFile.get(0).Observation)== null)
        {
        	System.err.println(input.sequenceinFile.get(0).Observation + " = null at observationSTStateCount.get(input.sequenceinFile.get(0).Observation)");
        	return null;
        }
        Set set1 = hmmParameters.observationSTStateCount.get(input.sequenceinFile.get(0).Observation).keySet();
		Iterator iter1 = set1.iterator();
        while (iter1.hasNext()) {
        	currState = (Character) (iter1.next());
        	tmpDelta = hmmParameters.lookupPI(currState) * hmmParameters.lookupB(currState, input.sequenceinFile.get(0).Observation);
        	deltaElement.put(currState, tmpDelta);
        	psiElement.put(currState, (char)0);
        }
        
//        Set set1 = hmmParameters.firstStateCount.keySet();
//        Iterator iter1 = set1.iterator();
//        while (iter1.hasNext()) {
//            currState = (Integer) (iter1.next());
//            tmpDelta = hmmParameters.lookupPI(currState) * hmmParameters.lookupB(currState, input.get(0).Observation);
//            deltaElement.put(currState, tmpDelta);
//            psiElement.put(currState, 0);
//        }

        delta.add(deltaElement);
        psi.add(psiElement);


        char prevState;
        char maxarg;
        double maxDelta;
        Set set2;
        Iterator iter2;

        //============================= Recursion
        for (int i = 1; i < input.size(); i++) {
            deltaElement = new Hashtable<Character, Double>();
            psiElement = new Hashtable<Character, Character>();

            if (hmmParameters.observationSTStateCount.get(input.get(i).Observation)== null)
            {
            	System.err.println(input.get(i).Observation + " = null at observationSTStateCount.get(input.get(i).Observation)");
            	continue;
            }
            set1 = hmmParameters.observationSTStateCount.get(input.get(i).Observation).keySet();
            iter1 = set1.iterator();
            // traversing the states I that has observation input.get(i)
            while (iter1.hasNext()) {
                maxDelta = 0;
                maxarg = (char) -1;
                currState = (Character) (iter1.next());
                //TODO: check null here.
                if (hmmParameters.stateTransitionCount.get(currState)==null)
                {
                	System.err.println(currState + " = null at stateTransitionCount");
                	continue;
                }
                set2 = hmmParameters.stateTransitionCount.get(currState).keySet();
                iter2 = set2.iterator();
                // traversing the states that can transfer to state I
                while (iter2.hasNext()) {
                    prevState = (Character) (iter2.next());
                    tmpDelta = getHashtableNullD(delta.get(i - 1), prevState) * hmmParameters.lookupA(prevState, currState);
                    if (tmpDelta > maxDelta) {
                        maxDelta = tmpDelta;
                        maxarg = prevState;
                    }
                }
                if (maxarg != (char)(-1)) {
                    deltaElement.put((currState), maxDelta);
                    psiElement.put(currState, maxarg);
                }
            }

            delta.add(deltaElement);
            psi.add(psiElement);
        }

        //========================== Termination

        Hashtable<Character, Double> lastDelta = delta.getLast();
        if (lastDelta.size() == 0) {
           return null;
        }

        maxDelta = 0;
        maxarg = (char)-1;
        set1 = lastDelta.keySet();
        iter1 = set1.iterator();
        while (iter1.hasNext()) {
            currState = (Character) iter1.next();
            if (lastDelta.get(currState) > maxDelta) {
                maxDelta = lastDelta.get(currState);
                maxarg = currState;
            }
        }

        //=========================== Backtracking
        input.getLast().State = maxarg;
        for(int i = input.size() - 2; i >= 0; i--)
        {
            input.get(i).State = getHashtableNullC(psi.get(i + 1), input.get(i + 1).State);
        }
        return input;
    }

    private double getHashtableNullD(Hashtable<Character, Double> table, char key) {
        
        if (table.containsKey(key)) {
            return table.get(key);
        }
        return 0;
    }

    private char getHashtableNullC(Hashtable<Character, Character> table, char key) {
        if (table.containsKey(key)) {
            return table.get(key);
        }
        return 0;
    }
}
